#include <GameEngine/UnitTest/unit_test.h>

#include <GameEngine/Geometry/Surfaces/circle2d.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


//! Launch the unit tests on Circle2d objects
/*!
 @return True if all unit tests on Circle2d objects were
         succesful, false otherwise
 */
template <class T>
bool UnitTest::test_geometry_circle2d_functions() {
	bool res = true;

	// Get number of bytes
	MemoryManager::BytesCount bytes = MemoryManager::instance()->bytes();

	// Create line
	GameEngine::Point2d<T> center((T)0,(T)0);
	T radius = (T)5;
	GameEngine::Circle2d<T> circle(center,radius);

	// Test accessors
	T tolerance = (T)0.00001;
	{{
		res &= UNIT_TEST_ERROR( "[Circle2d] Test center()", ( center.get_square_distance(circle.center()) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test radius()", ( abs(circle.radius()-radius) < tolerance ) );
	}}

	// Test diameter and area functions
	{{
		res &= UNIT_TEST_ERROR( "[Circle2d] Test diameter()", ( abs(circle.diameter()-(T)2*radius) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test area()", ( abs(circle.area()-(T)PI*radius*radius) < tolerance ) );
	}}

	// Test inside function
	{{
		GameEngine::Point2d<T> p1((T)1.5,(T)0.5);
		GameEngine::Point2d<T> p2((T)5.5,(T)0.5);
		GameEngine::Point2d<T> p3((T)radius,(T)0.0);
		res &= UNIT_TEST_ERROR( "[Circle2d] Test inside()", ( circle.inside(p1) == true ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test inside()", ( circle.inside(p2) == false ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test inside()", ( circle.inside(p3, true) == false ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test inside()", ( circle.inside(p3, false) == true ) );
	}}

	// Test intersection function
	{{
		GameEngine::Point2d<T> p1((T)0,(T)0);
		GameEngine::Point2d<T> p2((T)1,(T)1);
		GameEngine::Line2d<T> line(p1,p2);
		T coord1, coord2;
		GameEngine::IntersectionType::Type type;
		res &= UNIT_TEST_ERROR( "[Circle2d] Test intersection()", ( circle.intersection(line, coord1, coord2, &type) == 2 ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test intersection()", ( abs(coord1+radius/(T)sqrt(2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test intersection()", ( abs(coord2-radius/(T)sqrt(2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test intersection()", ( type == GameEngine::IntersectionType::Intersection ) );
	}}

	// Test intersection function
	{{
		GameEngine::Point2d<T> p1((T)0,radius);
		GameEngine::Point2d<T> p2((T)1,radius);
		GameEngine::Line2d<T> line(p1,p2);
		T coord1, coord2;
		GameEngine::IntersectionType::Type type;
		res &= UNIT_TEST_ERROR( "[Circle2d] Test intersection()", ( circle.intersection(line, coord1, coord2, &type) == 1 ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test intersection()", ( coord1 < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test intersection()", ( coord2 < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test intersection()", ( type == GameEngine::IntersectionType::TangentIntersection ) );
	}}

	// Test intersection function
	{{
		GameEngine::Point2d<T> p1((T)0,radius+(T)1);
		GameEngine::Point2d<T> p2((T)1,radius+(T)1);
		GameEngine::Line2d<T> line(p1,p2);
		T coord1, coord2;
		GameEngine::IntersectionType::Type type;
		res &= UNIT_TEST_ERROR( "[Circle2d] Test intersection()", ( circle.intersection(line, coord1, coord2, &type) == 0 ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test intersection()", ( type == GameEngine::IntersectionType::NoIntersection ) );
	}}

	// Test closest point function
	{{
		GameEngine::Point2d<T> pt((T)10,(T)10);
		GameEngine::Point2d<T> cpt = circle.closest_point(pt);
		res &= UNIT_TEST_ERROR( "[Circle2d] Test closest_point()", ( circle.closest_point(pt).get_square_distance(
			GameEngine::Point2d<T>((T)sqrt(2.0)*radius/(T)2,(T)sqrt(2.0)*radius/(T)2)) < tolerance ) );
	}}

	// Test point function
	{{
		GameEngine::Angle<T> angle1((T)0, GameEngine::Angles::Deg);
		GameEngine::Angle<T> angle2((T)90, GameEngine::Angles::Deg);
		GameEngine::Angle<T> angle3((T)180, GameEngine::Angles::Deg);
		GameEngine::Angle<T> angle4((T)270, GameEngine::Angles::Deg);
		res &= UNIT_TEST_ERROR( "[Circle2d] Test point()", ( circle.point(angle1).get_square_distance(GameEngine::Point2d<T>(radius,(T)0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test point()", ( circle.point(angle2).get_square_distance(GameEngine::Point2d<T>((T)0,radius)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test point()", ( circle.point(angle3).get_square_distance(GameEngine::Point2d<T>(-radius,(T)0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle2d] Test point()", ( circle.point(angle4).get_square_distance(GameEngine::Point2d<T>((T)0,-radius)) < tolerance ) );
	}}

	// Get number of bytes
	res &= UNIT_TEST_ERROR( "[Circle2d] Test memory leak", ( bytes == MemoryManager::instance()->bytes() ));

	// Return result
	return res;
}


#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
